package com.firewolf.hibernate.relationship.many_one_both.main;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.firewolf.hibernate.relationship.many_one_both.dto.Customer;
import com.firewolf.hibernate.relationship.many_one_both.dto.Order;

public class MainDriver {

	private SessionFactory factory;

	private Session session;

	private Transaction transaction;
	
	/**
	 * 双向多对1查询：(先查多的一端)
	 * 1.若查询多的一端的一个对象, 则默认情况下, 只查询了多的一端的对象. 而没有查询关联的 1 的那一端的对象!，
	 *   此时1的那一端的只是一个代理对象，等到真正需要使用的时候，才会真正发送对应的Sql语句；
	 * 2.如果通过多的一端获取一的一端的对象的时候，如果session已经关闭，则在默认情况下，会抛出LazyInitializationException异常；
	 */
	@Test
	public void testMany2OneBothGet(){
		openSession();
		
		//查询一个多的一端的对象
		Order order = (Order) session.get(Order.class, 1);
		System.out.println(order.getOrderName()); 
		//此时产生的是一个代理对象
		System.out.println(order.getCustomer().getClass().getName());
		
		//此时若关闭session，则在后面获取的时候会抛出异常
//		session.close();
		
		//在真正使用一的一端的对象的时候才发送对应的 SQL 语句. 
		Customer customer = order.getCustomer();
		System.out.println(customer.getCustomerName()); 
		
		
		closeSession();
	}
	
	
	/**
	 * 双向多对一删除：
	 * 1.在不设定级联关系的情况下, 且 1 这一端的对象有 n 的对象在引用, 不能直接删除 1 这一端的对象
	 */
	@Test
	public void testDelete(){
		openSession();
		Customer customer = (Customer) session.get(Customer.class, 1);
		session.delete(customer);
		closeSession();
	}
	/**
	 * 双向多对1修改，没什么特别
	 */
	@Test
	public void testUpdateBoth(){
		openSession();
		Order order = (Order) session.get(Order.class, 1);
		order.getCustomer().setCustomerName("AAA");
		closeSession();
	}
	
	/**
	 * 双向多对1查询：(先查1的一端)
	 * 1.对n的一端的集合使用延迟加载；在需要使用集合的时候进行加载
	 * 2.返回多的一端的集合石Hibernate内置的集合类型：org.hibernate.collection.internal.PersistentSet
	 *   该类型具有延迟加载和存放代理对象的功能；
	 *   所以在1的那段定义集合字段的时候，要使用接口，而不能使用具体的Java集合 
	 * 3.和先查多的一端的情况一样，也有可能出现懒加载异常
	 */
	@Test
	public void testMany2OneBothGet2(){
		openSession();
		
		Customer customer = (Customer) session.get(Customer.class, 1);
		
		System.out.println(customer.getCustomerName());
		
		//此时还是没有发送查询主对象的语句
		System.out.println(customer.getOrders().getClass().getName());
		
		//下面这条语句，会发送一条SQL语句
		System.out.println(customer.getOrders().size());
		closeSession();
	}

	/**
	 * @Description: 保存的时候，插入的顺序会影响SQL语句的条数
	 * @输入和输出示例：
	 * @思路：
	 */
	@Test
	public void testMany2OneBothSave(){
		openSession();
		
		Customer customer = new Customer();
		customer.setCustomerName("AAAA");
		
		Order order1 = new Order();
		order1.setOrderName("ORDER-111");
		
		Order order2 = new Order();
		order2.setOrderName("ORDER-222");
		
		//设定关联关系
		order1.setCustomer(customer);
		order2.setCustomer(customer);
		
		//设置一的一端的关联关系
		customer.getOrders().add(order1);
		customer.getOrders().add(order2);
		
		//先插入 Customer, 再插入 Order, 3 条 INSERT, 2 条 UPDATE
		session.save(customer);
		session.save(order1);
		session.save(order2);
		
		//先插入 Order, 再插入 Cusomer, 3 条 INSERT, 4 条 UPDATE
//		session.save(order1);
//		session.save(order2);
//		session.save(customer);
		
		closeSession();
	}
	

	private void openSession() {
		session = factory.openSession();
		transaction = session.beginTransaction();
	}
	
	private void closeSession() {
		transaction.commit();
		session.close();
	}

	@Before
	public void init() {
		Configuration cfg = new Configuration().configure("many_one_both.xml");

		ServiceRegistry registry = new ServiceRegistryBuilder().applySettings(cfg.getProperties())
				.buildServiceRegistry();
		factory = cfg.buildSessionFactory(registry);
	}

	@After
	public void destory() {
		factory.close();
	}
}
